SECTION core_data vstart = 0

    ram_alloc   dd 0x00100000                   ;内存分配指针，在00100000处开始分配内存
    core_buf   times 2048 db 0                  ;内核用的缓冲区

allocate_memory:                                ;分配内存
                                                ;输入：ECX=希望分配的字节数
                                                ;输出：ECX=起始线性地址
    push ds
    push eax
    push ebx

    mov eax,core_data_seg_sel
    mov ds,eax                                  ;ds寄存器指向了核心数据段

    mov eax,[ram_alloc]                         ;把内存分配的起始地址放到eax中
    add eax,ecx                                 ;eax加上希望分配的字节数，eax指向要分配的内存块的头部地址

    mov ecx,[ram_alloc]                         ;把内存分配的起始地址放到ecx中

    mov ebx,eax                                 ;将新计算的内存地址从eax复制到ebx
    and ebx,0xfffffffc                          ;清除ebx的最后两位，实现4字节对齐
    add ebx,4                                   ;在对齐后的地址再加上4
    test eax,0x00000003                         ;判断eax是否4字节对齐
    cmovnz eax,ebx                              ;如果不是4字节对齐，就将对齐后的地址放到eax中
    mov [ram_alloc],eax                         ;最终确定的内存分配地址保存回ram_alloc变量

    pop ebx
    pop eax
    pop ds

    retf

load_relocaate_program:                         ;加载重定位程序
                                                ;输入：push 逻辑扇区号
                                                ;     push 任务控制块基地址
                                                ;输出：无
    pushad

    push ds
    push es

    mov ebp,esp                                 ;通过ebp就能访问压栈之后的esp指针
    
    mov ecx,mem_0_4_gb_seg_sel
    mov es,ecx                                  ;es寄存器指向了0-4GB的内存

    mov esi,[ebp + 11*4]                        ;从堆栈中取得TCB的基地址
    ;以下申请创建LDT所需要的内存
    mov ecx,160                                 ;申请160字节的内存
    call sys_routine_seg_sel:allocate_memory
    mov [es:esi + 0x0c],ecx                    ;将申请到的内存地址保存到TCB的LDT基地址字段中   
    mov word [es:esi + 0x0a],0xffff            ;将申请到的内存地址保存到TCB的LDT限长字段中

    ;以下开始加载用户程序
    mov eax,core_data_seg_sel
    mov ds,eax                                  ;切换DS到内核数据段

    mov eax,[ebp + 12*4]                        ;从堆栈中取得逻辑扇区号
    mov ebx,core_buf                            ;将core_buf的地址放到ebx中
    call sys_routine_seg_sel:read_hard_disk_0

    ;以下判断整个程序有多大
    mov eax,[core_buf]                          ;程序尺寸
    mov ebx,eax
    and ebx,0xfffffe00                          ;使之512字节对齐（能被512整除）
    add ebx,512                                 ;在对齐后的地址再加上512
    test eax,0x000001ff                         ;程序的大小正好是512的倍数吗？
    cmovnz eax,ebx                              ;如果不是，就将对齐后的地址放到eax中

    mov ecx,eax                                 ;实际需要申请的内存数量
    call sys_routine_seg_sel:allocate_memory
    mov [es:esi + 0x06],ecx                     ;登记程序加载基地址到TCB中

    mov ebx,ecx                                 ;ebx ->申请到的内存首地址
    xor edx,edx
    mov ecx,512
    div ecx
    mov ecx,eax                                 ;总扇区数

    mov eax,mem_0_4_gb_seg_sel                  ;切换DS到0-4GB的内存
    mov ds,eax

    mov eax,[ebp + 12*4]                        ;起始扇区号
.b1:
    call sys_routine_seg_sel:read_hard_disk_0
    inc eax
    loop .b1                                   ;读取所有扇区

    mov edi,[es:esi + 0x06]                     ;获得程序加载基地址

    ;建立程序头部段描述符
    mov eax,edi                                 ;把程序加载的基地址放入eax寄存器
    mov ebx,[edi + 0x04]                        ;获取程序长度
    dec ebx                                     ;段界限
    mov ecx,0x0040f200